<!-- 单文件上传 支持存在一种文件  -->
<template>
  <el-upload
    ref="uploadRef"
    action="#"
    :limit="1"
    :on-exceed="handleExceed"
    :on-success="handleSuccess"
    :before-upload="beforeUpload"
    :on-change="handleChange"
    :on-remove="handleRemove"
    :auto-upload="false"
    :accept="accept"
    :show-file-list="showFileList"
    :class="{'single-upload': true, [className]: className}"
    :disabled="disabled"
    :style="containerStyle"
  >
    <div class="upload-trigger">
      <template v-if="modelValue">
        <img :src="modelValue" class="preview-img">
      </template>
      <template v-else>
        <div class="upload-content">
          <slot name="icon">
            <el-icon class="upload-icon">
              <Plus />
            </el-icon>
          </slot>
          <span class="upload-text">{{ placeholder || `上传${maxSize}M内 ${acceptText}` }}</span>
          <div class="upload-tip" v-if="!modelValue">
            <slot name="tip"></slot>
          </div>
        </div>
      </template>
    </div>
  </el-upload>
</template>

<script lang="ts" setup>
import { computed, ref } from 'vue'
import { UploadProps, UploadUserFile, UploadInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'

interface Props {
  modelValue?: string
  maxSize?: number
  accept?: string
  placeholder?: string
  className?: string
  showFileList?: boolean
  disabled?: boolean
  width?: number
  height?: number
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: '',
  maxSize: 5,
  accept: 'image/svg+xml,image/jpeg,image/png',
  placeholder: '',
  className: '',
  showFileList: false,
  disabled: false,
  width: 160,
  height: 160
})

const emit = defineEmits<{
  (e: 'update:modelValue', value: string): void
  (e: 'file-success', file: File): void
  (e: 'file-error', error: any): void
  (e: 'file-change', file: File | null): void
}>()

const uploadRef = ref<UploadInstance>()

// 计算容器样式
const containerStyle = computed(() => ({
  width: `${props.width}px`,
  height: `${props.height}px`
}))

// 格式化显示的文件类型文本
const acceptText = computed(() => {
  return props.accept.split(',').map(type => {
    return type.replace('image/', '').toUpperCase()
  }).join('/')
})

// 处理超出限制
const handleExceed: UploadProps['onExceed'] = (files) => {
  if (uploadRef.value) {
    uploadRef.value.clearFiles()
    const file = files[0] as UploadUserFile
    uploadRef.value.handleStart(file)
  }
}

// 处理文件移除
const handleRemove: UploadProps['onRemove'] = () => {
  emit('update:modelValue', '')
  emit('file-change', null)
}

// 上传前验证
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
  if (props.disabled) {
    ElMessage.warning('上传功能已禁用')
    return false
  }

  // 检查文件大小
  if (rawFile.size / 1024 / 1024 > props.maxSize) {
    ElMessage.error(`文件大小不能超过 ${props.maxSize}MB!`)
    emit('file-error', new Error(`File size exceeds ${props.maxSize}MB`))
    return false
  }
  
  // 检查文件类型
  if (!props.accept.includes(rawFile.type)) {
    ElMessage.error(`只支持 ${acceptText.value} 格式!`)
    emit('file-error', new Error(`Unsupported file type`))
    return false
  }

  return true
}

// 文件改变时的回调
const handleChange: UploadProps['onChange'] = (uploadFile) => {
  if (uploadFile.raw) {
    const url = URL.createObjectURL(uploadFile.raw)
    emit('update:modelValue', url)
    emit('file-change', uploadFile.raw)
  }
}

// 上传成功的回调
const handleSuccess: UploadProps['onSuccess'] = (
  response,
  uploadFile
) => {
  if (uploadFile.raw) {
    emit('file-success', uploadFile.raw)
  }
}

// 暴露方法给父组件
defineExpose({
  clearFiles: () => {
    uploadRef.value?.clearFiles()
    emit('update:modelValue', '')
    emit('file-change', null)
  }
})
</script>

<style lang="scss" scoped>
.single-upload {
  display: inline-block;
  border: 1px solid var(--el-border-color);
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: var(--el-transition-duration-fast);

  &:hover {
    border-color: var(--el-color-primary);
  }

  &:deep(.el-upload) {
    display: block;
    width: 100%;
    height: 100%;
  }

  .upload-trigger {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
  }

  .upload-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    width: 100%;
    position: relative;
    padding: 8px;
  }

  .preview-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
  }
  
  .upload-icon {
    font-size: 28px;
    color: var(--el-text-color-secondary);
    margin-bottom: 8px;
  }

  .upload-text {
    font-size: 12px;
    color: var(--el-text-color-secondary);
    text-align: center;
  }

  .upload-tip {
    position: absolute;
    bottom: 8px;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 12px;
    color: var(--el-text-color-secondary);
  }
}
</style> 